import { Service, Container } from 'typedi';
import { CreateRoleDto, RolePageDto, UpdateRoleDto } from '@/dtos';
import { RolePageResult, RoleWithMenuIds } from '@/interfaces';
import { PrismaClient } from '@/generated/prisma';
import { RoleExistedException, RoleNotFoundException, ServerErrorException } from '@/exceptions';

@Service()
export class RoleService {
  public prisma: PrismaClient = Container.get('PRISMA_CLIENT');

  async findAll(pageDto: RolePageDto): Promise<RolePageResult> {
    const where = {
      ...(pageDto.name && { name: { contains: pageDto.name } }),
      ...(pageDto.code && { code: { contains: pageDto.code } }),
      ...(pageDto.status !== undefined && { status: pageDto.status }),
    };
    const [list, total] = await Promise.all([
      this.prisma.role.findMany({
        skip: pageDto.offset,
        take: pageDto.pageSize,
        where,
        orderBy: { id: 'asc' },
      }),
      this.prisma.role.count({ where }),
    ]);
    return { list, total };
  }

  async findOne(code: string): Promise<RoleWithMenuIds | null> {
    const result = await this.prisma.role.findFirst({
      where: { code },
      include: {
        menus: {
          select: {
            menuId: true,
          },
        },
      },
    });
    if (!result) return null;
    const { menus, ...role } = result;
    return {
      ...role,
      menuIds: menus.map(m => m.menuId),
    };
  }

  async create(dto: CreateRoleDto): Promise<void> {
    // 检测角色名称或编码是否已存在
    const role = await this.prisma.role.findFirst({
      where: {
        OR: [{ name: dto.name }, { code: dto.code }],
      },
    });
    if (role) throw new RoleExistedException();
    await this.prisma.role.create({
      data: {
        code: dto.code,
        name: dto.name,
        remark: dto.remark,
        status: dto.status,
        menus: {
          createMany: {
            data: dto.menuIds.map(menuId => ({ menuId })),
          },
        },
      },
    });
  }

  async update(code: string, dto: UpdateRoleDto): Promise<void> {
    await this.prisma.$transaction(async tx => {
      // 校验角色是否存在
      const role = await tx.role.findFirst({ where: { code } });
      if (!role) throw new RoleNotFoundException();
      // 名称/编码冲突检测
      const conflict = await tx.role.findFirst({
        where: {
          OR: [...(dto.name ? [{ name: dto.name }] : []), ...(dto.code ? [{ code: dto.code }] : [])],
          NOT: { id: role.id },
        },
      });
      if (conflict) throw new RoleExistedException();
      // 过滤掉不存在的 menuId
      const validMenus = await tx.menu.findMany({
        where: { id: { in: dto.menuIds || [] } },
        select: { id: true },
      });
      const validMenuIds = validMenus.map(m => m.id);
      // 删除旧关系
      await tx.roleMenu.deleteMany({ where: { roleId: role.id } });
      // 写入新关系
      if (validMenuIds.length) {
        await tx.roleMenu.createMany({
          data: validMenuIds.map(menuId => ({ roleId: role.id, menuId })),
        });
      }
      // 更新角色字段
      await tx.role.update({
        where: { code },
        data: {
          code: dto.code,
          name: dto.name,
          remark: dto.remark,
          status: dto.status,
        },
      });
    });
  }

  async remove(code: string): Promise<void> {
    try {
      await this.prisma.role.delete({ where: { code } });
    } catch (e: any) {
      // 无记录删除
      if (e.code === 'P2025') return;
      throw new ServerErrorException(e);
    }
  }
}
